Skip to content

2804. call vs apply vs bind

Problem: 2804. 数组原型的 forEach 方法

js
/**
 * apply
 */
Array.prototype.forEach = function (callback, context) {
    const values = this;
    for (let i = 0; i < values.length; i++) {
        callback.apply(context, [values[i], i, values])
    }
}

/**
 * call
 */
Array.prototype.forEach = function (callback, context) {
    const values = this;
    for (let i = 0; i < values.length; i++) {
        callback.call(context, values[i], i, values)
    }
}

/**
 * bind - 参数可以预绑定
 */
Array.prototype.forEach = function (callback, context) {
    const values = this;
    for (let i = 0; i < values.length; i++) {
        callback.bind(context, values[i], i, values)();
    }
}

三者唯一的本质区别

方法是否立即执行传参方式
call逗号分隔
apply数组
bind逗号分隔(可分批)

callapply 的区别主要在传参格式call(ctx, a, b, c) vs apply(ctx, [a, b, c])。选择哪个,通常取决于参数当前的自然形态。

bind 与前两者的本质区别在于:不执行函数,返回一个新函数。这个新函数可以在之后任意时机调用,this 和部分参数已被锁定。

在 forEach 中的应用

forEach 为例,三者都能实现回调的 this 绑定:

  • call 最直接,逐个传参,可读性最好
  • apply 需要把参数包成数组,多一次数组创建
  • bind 做了两次函数调用(bind 一次 + 执行一次),在循环中开销最大

在这类 forEach polyfill 里,call 往往是更直接的选择。bind 在循环中会额外创建函数对象,除非确实需要延迟执行,否则没有明显收益。

call/apply vs 现代写法

ES6 之后,扩展运算符让 apply 的使用场景大幅减少:fn.apply(ctx, arr) 等价于 fn.call(ctx, ...arr)。现代代码更倾向于 call + 扩展运算符,因为意图更明确(不是在「应用数组」,而是在「展开数组传参」)。